package lp.polimi.treefinder;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.content.Context;

/**
 * Manages writing/reading/updating operations on the tagged leaves XML file.
 */
public class XMLHandler {

	static final String KEY_LEAF = "leaf";
	static final String KEY_NAME = "name";
	static final String KEY_DATE = "date";
	static final String KEY_ADDRESS = "address";
	static final String KEY_CITY = "city";
	static final String KEY_COUNTRY = "country";
	static final String KEY_UNKNOWN_ID = "unknown_id";


	private XMLHandler () {
	}

	/**
	 * Reads the XML file and returns a model of it.
	 */
	public static Document getDomElement(Context context, String fileName) throws SAXException, IOException, ParserConfigurationException{

		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		return  documentBuilder.parse(new File(context.getExternalFilesDir(null), fileName));
	}

	/**
	 * @return The content of an attribute of the leaf.
	 */
	public static String getValue(Element item, String str) {  
		Node n = item.getAttributes().getNamedItem(str);
		if (n!=null)
			return item.getAttributes().getNamedItem(str).getNodeValue();
		return null;
	}

	/**
	 * Add a new leaf to the model and writes the updated model to file (overwriting the previous one).
	 * @param unknownListIdentifier This parameter is passed if the tagging procedure hasn't been successful
	 * for some reason and the user has decided to store the picture and perform the tag later. It's used to
	 * distinguish between multiple untagged leaves that the user could have decided to store.
	 */
	public static void writeXMLFile (Context context, String leafName, String date,
			String address, String city, String country, String unknownListIdentifier) {

		try {
			Document doc = getDomElement(context, "leaves.xml");
			Node root = doc.getElementsByTagName("leaves").item(0);

			//create a new leaf element and add it to the model
			Element leaf = doc.createElement("leaf");
			root.appendChild(leaf);

			//fill the new leaf element with new attributes, whom values are passed as input.
			setNewLeaf(leafName, date, address, city, country,
					unknownListIdentifier, leaf);

			//write XML
			writeToFile(context, "leaves.xml", doc);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Add new attributes to the leaf element, whom values are passed as input.
	 */
	private static void setNewLeaf(String leafName, String date,
			String address, String city, String country,
			String unknownListIdentifier, Element leaf) {

		leaf.setAttribute(KEY_NAME, leafName);
		leaf.setAttribute(KEY_DATE, date);
		if (unknownListIdentifier!=null)
			leaf.setAttribute(KEY_UNKNOWN_ID, unknownListIdentifier);
		if (address!=null)
			leaf.setAttribute(KEY_ADDRESS, address);
		if (city!=null)
			leaf.setAttribute(KEY_CITY, city);
		if (country!=null)
			leaf.setAttribute(KEY_COUNTRY, country);
	}

	/**
	 * Write the XML.
	 * @param doc The source document to be written.
	 */
	private static void writeToFile(Context context, String fileName,
			Document doc) throws TransformerFactoryConfigurationError,
			TransformerConfigurationException, TransformerException {

		// write content into xml file
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(new File(context.getExternalFilesDir(null), fileName));

		transformer.transform(source, result);
	}

	/**
	 * Used to update an untagged leaf which has been tagged.
	 * Reads the XML, find the node having the id passed as input and update it.
	 * @param unknownLeafId The id of the untagged leaf.
	 * @param realName The just retrieved species name.
	 */
	public static void updateXMLFile(Context context, String unknownLeafId, String realName) {

		try {

			Document doc = getDomElement(context, "leaves.xml");
			Node root = doc.getElementsByTagName("leaves").item(0);
			NodeList nl = doc.getElementsByTagName(KEY_LEAF);

			for (int i = 0; i < nl.getLength(); i++) {
				Element e = (Element) nl.item(i);
				String currUnknownId = getValue(e, KEY_UNKNOWN_ID);

				if (currUnknownId!=null && currUnknownId.equals(unknownLeafId)) {
					Element leaf = doc.createElement("leaf");
					setNewLeaf(realName, getValue(e, KEY_DATE), getValue(e, KEY_ADDRESS),
							getValue(e, KEY_CITY), getValue(e, KEY_COUNTRY), null, leaf);
					root.replaceChild(leaf, e);
				}
			}

			writeToFile(context, "leaves.xml", doc);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
